package cn.gx.kevin.common.utils;

import cn.gx.kevin.common.annotation.*;
import cn.gx.kevin.common.thread.LangThreadHodler;
import cn.gx.kevin.common.ui.LanguageMap;
import cn.gx.kevin.common.ui.LanguageStore;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * @ClassName: AnnotationValid
 * @Description: 基于实体bean的注解验证
 * @author kevin.huang
 * @date 2015-1-16 上午11:13:10
 * @param <T>
 * @param <K>
 */
public class AnnotationValid<T extends Serializable, K extends Serializable> {
	private String pName;
	private Class<?> clazz;

	/**
	 * @Title: Valid
	 * @Description: 调用数据库的唯一性验证
	 * @param obj            待验证的对象
	 * @param excFileds      动态解决实体bean已经打上验证，但又不需要验证的字段
	 * @param svcValuid       验证服务
	 * @param request         请求参数对象
	 * @return 验证信息，如果验证通过返回空字符串
	 */
	public String valid(T obj, HashSet<String> excFileds, IServiceValid svcValuid, HttpServletRequest request)
			throws Throwable {
		StringBuffer validRes = new StringBuffer();
		clazz = obj.getClass();
		Field[] Objfields = clazz.getDeclaredFields();
		Map<String, String> langMap = LanguageStore.getLangHashMap("");
		Method getIdMethod = null;
		try {
			getIdMethod = clazz.getMethod("getId");
		}catch(Exception x) {
		}
		for (Field f : Objfields) {
			pName = f.getName();
			if ("serialVersionUID".equals(pName) || "id".equals(pName)
					|| excFileds != null && excFileds.contains(pName)) {
				continue;
			}
			/** 是否必填 **/
			Require require = f.getAnnotation(Require.class);
			if (require != null) {
				String requireRes = validRequire(obj, require, f, langMap);
				validRes.append(requireRes);
			}
			/** 正则表达式 **/
			RegexValid regex = f.getAnnotation(RegexValid.class);
			if (regex != null) {
				Object v = getValue(obj, f);
				if (v != null && !String.valueOf(v).equals("")) {
					validRes.append(validRegex(obj, regex, f, langMap));
				}
			}
			/**
			 * 最大输入长度
			 ***/
			MaxLength mlen = f.getAnnotation(MaxLength.class);
			if (mlen != null) {
				Object v = getValue(obj, f);
				if (v != null && !String.valueOf(v).equals("")) {
					int len = mlen.maxlen();
					int char_len = getLength(obj, f);
					if (char_len > len) {
						String msg = mlen.langKey();
						if(langMap != null && langMap.containsKey(msg)){
							msg = langMap.get(msg);
						}
						validRes.append(msg + "<br/>");
					}
				}
			}
			/***
			 * 最小输入长度
			 **/
			MinLength minlen = f.getAnnotation(MinLength.class);
			if (minlen != null) {
				Object v = getValue(obj, f);
				if (v != null && !String.valueOf(v).equals("")) {
					int len = minlen.minlen();
					int char_len = getLength(obj, f);
					if (char_len < len) {
						String msg = minlen.langKey();
						if(langMap != null && langMap.containsKey(msg)){
							msg = langMap.get(msg);
						}
						validRes.append(msg + "<br/>");
					}
				}
			}

			/***
			 * 输入数值范围
			 ***/
			Range range = f.getAnnotation(Range.class);
			if (range != null) {
				Object v = getValue(obj, f);
				if (v != null) {
					double min = range.min();
					double max = range.max();
					double val = Double.valueOf(String.valueOf(v));
					if (val < min || val > max) {
						String msg = range.langKey();
						if(langMap != null && langMap.containsKey(msg)){
							msg = langMap.get(msg);
						}
						msg = msg.replaceAll("\\{min\\}", String.valueOf(min));
						msg = msg.replaceAll("\\{max\\}", String.valueOf(max));
						validRes.append(msg + "<br/>");
					}
				}
			}
			/** 数据库唯一性验证 **/
			Uniquely uniquely = f.getAnnotation(Uniquely.class);
			if (uniquely != null && svcValuid != null) {
				Object v = getValue(obj, f);
				if (v != null && !String.valueOf(v).equals("")) {
					validRes.append(validUniquely(obj, uniquely, f, svcValuid, request, langMap, getIdMethod));
				}
			}
		}
		return validRes.toString();
	}

	private Map<String, Object> fieldValueCache = new HashMap<String, Object>();
	private Set<String> flagSet = new HashSet<String>();

	private Object getValue(T obj, Field f) throws Throwable {
		if (flagSet.contains(pName)) {
			return fieldValueCache.get(pName);
		}
		PropertyDescriptor pd = new PropertyDescriptor(pName, clazz);
		Method wM = pd.getReadMethod();
		Object v = wM.invoke(obj);
		fieldValueCache.put(pName, v);
		flagSet.add(pName);
		return v;
	}

	private int getLength(T obj, Field f) throws Throwable {
		Object v = getValue(obj, f);
		if (v != null) {
			String value = String.valueOf(v);
			return value.getBytes("GB2312").length;
		}
		return 0;
	}

	private String validRequire(T obj, Require require, Field f, Map<String, String> langMap) throws Throwable {
		String strRes = "";
		Object value = getValue(obj, f);
		String langKey = require.langKey();
		if(langMap != null && langMap.containsKey(langKey)){
			if (value == null ) {
				strRes = langMap.get(langKey);
			} else {
				String val = String.valueOf(value);
				if ("".equals(val.trim())) {
					strRes = langMap.get(langKey);
				}
			}
		}else if(value == null || StringUtils.isEmpty(String.valueOf(value))){
			strRes = langKey;
		}
		return "".equals(strRes) ? "" : strRes + "<br/>";
	}

	private String validRegex(T obj, RegexValid regex, Field f, Map<String, String> langMap) throws Throwable {
		String strRes = "";
		Object value = getValue(obj, f);
		String regexExp = regex.regText();
		Pattern pattern = Pattern.compile(regexExp);
		boolean isOk = pattern.matcher(String.valueOf(value)).matches();
		if (!isOk) {
			String langKey = regex.langKey();
			if(langMap != null && langMap.containsKey(langKey)){
				strRes = langMap.get(langKey);
			}else{
				strRes = langKey;
			}

		}
		return "".equals(strRes) ? "" : strRes + "<br/>";
	}

	private String validUniquely(T obj, Uniquely uniquely, Field f, IServiceValid svcValuid, HttpServletRequest request,
								 Map<String, String> langMap,Method getIdMethod) throws Throwable {
		String strRes = "";
		String extFiledstring = uniquely.extField();
		String[] extFileds = extFiledstring.split(",");
		Object value = getValue(obj, f);
		String val = String.valueOf(value);
		if (val == null || "".equals(val.trim())) {
			return "";
		}
		Map<String, Object> paramMap = new HashMap<String, Object>();
		paramMap.put(pName, value);
		if(getIdMethod != null) {
			Object idValObject = getIdMethod.invoke(obj);
			if(idValObject != null && StringUtils.isNotEmpty(String.valueOf(idValObject))) {
				paramMap.put("_selfId",idValObject);
			}
		}
		for (String p : extFileds) {
			if ("".equals(p)) {
				continue;
			}
			PropertyDescriptor pd1 = new PropertyDescriptor(p, clazz);
			Method wM1 = pd1.getReadMethod();
			wM1.setAccessible(true);
			Object value1 = wM1.invoke(obj);
			paramMap.put(p, value1);
		}
		boolean isPast = svcValuid.serviceValid(paramMap);
		if (!isPast) {
			String langKey = uniquely.langKey();
			if(langMap != null && langMap.containsKey(langKey)){
				strRes = langMap.get(langKey).replace("{"+pName+"}",String.valueOf(value));
			}else{
				strRes = langKey.replace("{"+pName+"}",String.valueOf(value));
			}
		}
		return "".equals(strRes) ? "" : strRes + "<br/>";
	}
}
